package com.shengwei.truck.driver.socket;
import android.content.Context;
import android.util.Log;

import com.shengwei.truck.driver.socket.dataconvert.ByteArrayCodecFactory;
import com.shengwei.truck.driver.socket.dataconvert.ByteUtils;
import com.shengwei.truck.driver.utils.CommonUtil;
import com.shengwei.truck.driver.utils.MyLogFile;
import org.apache.mina.core.filterchain.IoFilterAdapter;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.service.IoConnector;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.transport.socket.SocketSessionConfig;
import org.apache.mina.transport.socket.nio.NioSocketConnector;
import java.net.InetSocketAddress;

/**
 * MINA socket Tcp 客户端
 */
public class TcpClient extends IoHandlerAdapter {
    String TAG = "TcpClient";
    static public  IoConnector mConnector; //相当于tcp socket client对象
    static public  IoSession mSession;    //负责mConnector的读写数据
    public static  boolean mIsReConn = true; //是否需要重连
    static int reConnTimes = 1;
    Context mContext;

    /**
     * 创建Socket客户端并连接服务器:IP和PORT
     * @param ip 服务器Tcp Server IP
     * @param port 服务器Tcp Server PORT
     * @param context
     */
    public TcpClient(final String ip, final int port, Context context) {
        this.mContext = context;
        mConnector =  getConnectorInstance(ip,port,mContext);
    }
    /**
     * 配置socket客户端的session的 空闲超时与收发缓冲区大小
     */
    public void configSocketClient(IoConnector connector) {
        //配置缓冲区大小，超时时间等
        SocketSessionConfig sessionConfig = (SocketSessionConfig) mConnector.getSessionConfig();
        sessionConfig.setIdleTime(IdleStatus.BOTH_IDLE, 295); //读写都空闲超过5分钟，则重新连接服务器
        sessionConfig.setReceiveBufferSize(1024);
        sessionConfig.setSendBufferSize(1024);
        //tcp socket client发送的数据格式是byte
        mConnector.getFilterChain().addLast("codeByte", new ProtocolCodecFilter(new ByteArrayCodecFactory()));
    }

    /**
     * 创建连接器connector对象,这个相当于tcp socket client,并连接服务器.
     * @param ip：服务器TCP IP地址
     * @param port:服务器TCP port
     * @param context：android组件上下文
     * @return
     */
    public synchronized  IoConnector getConnectorInstance(final String ip, final int port, final Context context) {
        //创建连接器connector,这个相当于tcp socket client
        if (mConnector == null) {
            mConnector = new NioSocketConnector();
            // 配置tcp socket client的超时时间等
            configSocketClient(mConnector);

            // 监听连接断开状态，然后重连
            setListenerForReconnect(ip,port,context);
            mConnector.setHandler(this);
        }
        //开始连接服务器
        ConnectFuture connFuture = mConnector.connect(new InetSocketAddress(ip, port));
        connFuture.awaitUninterruptibly();
        mSession = connFuture.getSession();
        if(mSession == null || !mSession.isConnected()) {
            mConnector.dispose();
            mConnector = null;
        }
        return mConnector;
    }

    /**
     * 监听到连接断开，则内部默认生成一个子线程，然后去重连服务器。
     * @param
     * @param ip：服务器Tcp Server IP
     * @param port:服务器Tcp Server Port
     * @param context
     */
    public void setListenerForReconnect(final String ip,final int port,final Context context) {
        //监测connector连接，当session被关闭时，重新使用connector连接服务器
        if(mConnector != null) {
            mConnector.getFilterChain().addFirst("reconnection", new IoFilterAdapter() {
                @Override
                public void sessionClosed(NextFilter nextFilter, IoSession ioSession) throws Exception {
                    if(mConnector.isDisposed()) {
                        MyLogFile.i(TAG,"tempIoConnector.isDisposed() is true");
                        mConnector = null;
                        return;
                    }
                    while(mIsReConn){
                        try{
                            MyLogFile.e(TAG,"tcp client prepare reconnection in "+SocketManager.reConnInterval+" seconds threadId="+Thread.currentThread().getId());
                            Thread.sleep(SocketManager.reConnInterval*1000);  //这里出现了休眠时间过长的现象，本来1分钟就可以执行到下一行it has wake up,但是有时候休眠了8分钟，初步估计是CPU分时一直没有轮到它吧
                            MyLogFile.i(TAG,"it has waked up threadId="+Thread.currentThread().getId());

                            //已连接上直接返回
                            if(mSession !=null &&  mSession.isConnected()) {
                                MyLogFile.e(TAG,"has connected success!");
                                return;
                            }
                            MyLogFile.i(TAG,"mSession="+mSession);
                            boolean isNetAvailable = CommonUtil.isNetworkAvailable(context);

                            // 如果网络可用再重连
                            if(isNetAvailable){
                                //给以下2行代码添加同步关键字

                                MyLogFile.i(TAG,"   tcp client 第"+reConnTimes+"次网络OK,准备重连服务器threadId="+Thread.currentThread().getId());

                                // 连接服务器IP和PORT
                                ConnectFuture connFuture = mConnector.connect(new InetSocketAddress(ip, port));
                                connFuture.awaitUninterruptibly();
                                // 获取用于直接和服务器读写的session
                                mSession = connFuture.getSession();

                                // 判断是否连接失败，如果失败要释放资源
                                if(mSession == null || !mSession.isConnected()) {
                                    mConnector.dispose();
                                }
                                // 连接成功后，结束while循环，即结束重连
                                if(connFuture.isConnected()) {
                                    reConnTimes = 1;
                                    mIsReConn = false;
                                    Log.i(TAG,"tcp client 重连OK threadId="+Thread.currentThread().getId());
                                    break;
                                }
                            }
                            else {//网络不可用
                                MyLogFile.i(TAG,"tcp client 第"+reConnTimes+"次网络不可用");
                            }
                            reConnTimes++;
                        }catch(Exception ex){ //其它异常
                            MyLogFile.e(TAG,"  tcp client 重连服务器登录失败,60秒再连接一次:" + ex.getMessage());
                        }
                    }
                }
            });
        }
    }

    /**
     * socket连接服务器创建Session成功时回调
     * @param session
     * @throws Exception
     */
    @Override
    public void sessionCreated(IoSession session) throws Exception {
        MyLogFile.i(TAG,"  tcp client sessionCreated threadId="+Thread.currentThread().getId());
    }

    /**
     * socket连接服务器成功后回调该函数，Session可以与服务器通信了
     * @param session
     * @throws Exception
     */
    @Override
    public void sessionOpened(IoSession session) throws Exception {
        Log.i(TAG,"tcp client sessionOpened threadId="+Thread.currentThread().getId());
        SocketManager.getInstance(mContext).startHeartTask();
    }

    /**
     * socket连接断开时回调
     * @param session
     * @throws Exception
     */
    @Override
    public void sessionClosed(IoSession session) throws Exception {
        MyLogFile.i(TAG,"tcp client sessionClosed");
    }
    /**
     * 已发数据时回调，可以获取到已发送了什么数据
     * @param session
     * @param message
     * @throws Exception
     */
    @Override
    public void messageSent(IoSession session, Object message) throws Exception {

        byte[] sendBytes = (byte[]) message;
        String hexBytes = ByteUtils.byteToHexString(sendBytes);
        //gxw-MyLogFile.i(TAG,"  tcp client sent"+  hexBytes);
    }

    /**
     * 已接受数据时回调，可以获取到接收的数据
     * @param session
     * @param message
     * @throws Exception
     */
    @Override
    public void messageReceived(IoSession session, Object message) throws Exception {
        byte[] recvBytes = (byte[]) message;
        String hexBytes = ByteUtils.byteToHexString(recvBytes);
        MyLogFile.i(TAG,"  tcp client recv"+  hexBytes);
    }

    /**
     * 发送，接收等异常
     * @param session
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
        cause.printStackTrace();
        String errMsg = cause.getMessage();
        MyLogFile.e(TAG,"  exception tcp client exception msg = :" + errMsg);
        if(session != null){
            session.close(true);
        }
    }






    /**
     * {@inheritDoc}
     */
    @Override
    public void sessionIdle(IoSession session, IdleStatus status) throws Exception {

        MyLogFile.i(TAG,"  tcp client sessionIdle");
        if(session != null){
            session.close(true);
        }
    }


}
